<?php
declare(strict_types=1);

namespace lib\data;

use lib\data\virtual\Documents;
use lib\data\virtual\GoodsOrder;
use lib\pay\PayException;
use PDOException;
use sys\db\SubTask;
use sys\Log;
use Throwable;

class Store extends \lib\sync\SyncableData {
    use traits\MidTrait;
    use traits\WaitLock;

    protected static string $SyncKey = 'Store';
    protected static string $TableName = 'tp_nb_goods_store';

    const IS_RACK   = 1;      # 吧台货架
    const IS_STORE  = 2;      # 仓库

    protected int $id;
    protected int $mid;
    protected int $prop;
    protected string $name;
    protected int $sortid;
    protected int $createtime;
    protected bool $def;

    protected static array $propNamesC = ['id','mid','prop', 'name', 'sortid', 'createtime','def'];
    protected static array $propNamesU = ['id','mid','prop', 'name', 'sortid', 'createtime'];
    protected static array $propNamesJ = ['id','mid','prop', 'name', 'sortid', 'createtime','def'];


    public function __construct(array &$data)
    {
        parent::__construct($data);
        $this->traitInit();
    }

    public function getNumGoods() :int {
        $num_goods = 0;
        $goods = StoreComponents::getByStoreId($this->id);
        foreach($goods as $g){
            /** @var \lib\StoreComponents $g */
            $num_goods += $g->store_num;
        }
        return $num_goods;
    }

    public static function Move(int $mid , int $fromId, int $toId) :array {
        $from = \lib\data\Store::get($fromId);
        $to = \lib\data\Store::get($toId);

        if(null === $from || null === $to){
            return ['success'=>false, 'message'=>'仓库不存在.'];
        }

        if($from->id === $to->id){
            return ['success'=>false, 'message'=>'不能移动到自己.'];
        }

        if($from->mid !== $mid || $to->mid !== $mid){
            return ['success'=>false, 'message'=>'仓库不存在.'];
        }

        $toSortId = $to->sortid;
        $moveFront = $from->sortid > $toSortId;
        $syncObject = new \lib\sync\Synchronize($mid);
        $db = new \sys\Db();
        $db->startTrans();
        try{
            $db->table('tp_nb_goods_store')->where([['id', '=', $from->id]])->lock(true)->field('id')->find();
            if($moveFront){
                # 前移
                $moves= array_column($db->table('tp_nb_goods_store')->where([['mid', '=', $mid], ['sortid', 'BETWEEN', [$toSortId, $from->sortid-1]]])->lock(true)->field('id')->select() ?? [], 'id');
                $sqls = [
                    $db->table('tp_nb_goods_store')->where([['mid', '=', $mid], ['sortid', 'BETWEEN', [$toSortId, $from->sortid-1]]])->inc('sortid', 1)->subUpdate(),
                    $db->table('tp_nb_goods_store')->where([['id', '=', $from->id]])->subUpdate(['sortid'=>$toSortId]),
                ];
            }else{
                # 后移
                $moves= array_column($db->table('tp_nb_goods_store')->where([['mid', '=', $mid], ['sortid', 'BETWEEN', [$from->sortid+1, $toSortId]]])->lock(true)->field('id')->select() ?? [], 'id');
                $sqls = [
                    $db->table('tp_nb_goods_store')->where([['mid', '=', $mid], ['sortid', 'BETWEEN', [$from->sortid+1, $toSortId]]])->dec('sortid', 1)->subUpdate(),
                    $db->table('tp_nb_goods_store')->where([['id', '=', $from->id]])->subUpdate(['sortid'=>$toSortId]),
                ];
            }
            $db->batch_query($sqls, \sys\Db::SQL_UPDATE);
            $db->commit();

            if($moveFront){
                # 前移
                foreach($moves as $id){
                    $gds = \lib\data\Store::get($id);
                    $gds->sortid += 1;
                    $syncObject->up('Store', $gds->id, ['sortid'=>$gds->sortid]);
                }
            }else{
                # 后移
                foreach($moves as $id){
                    $gds = \lib\data\Store::get($id);
                    $gds->sortid -= 1;
                    $syncObject->up('Store', $gds->id, ['sortid'=>$gds->sortid]);
                }
            }
            $from->sortid = $toSortId;
            $syncObject->up('Store', $from->id, ['sortid'=>$from->sortid]);
            \lib\Merchant::get($mid)->push(['event'=>'sync','data'=>$syncObject->makeSyncData($db)]);
            return ['success'=>true, 'message'=>'调整成功.'];
        }catch(\Exception $e){
            $db->rollback();
            return ['success'=>false, 'message'=>$e->getMessage()];
        }
    }

    public static function save(int $mid, array $data) :array {

        $db = new \sys\Db();

        if(isset($data['id'])){
            $store = static::get((int)$data['id']);
            if(null == $store || $store->mid != $mid){
                return ['success'=>false, 'message'=>'仓库不存在.'];
            }
            if(null == ($changed = $store->update(['name'=>$data['name']]))){
                return ['success'=>false, 'message'=>'没有修改.'];
            }
            $db->table('tp_nb_goods_store')->where([['id','=',$store->id]])->update($changed);
            $syncObject = new \lib\sync\Synchronize($mid);
            $syncObject->up('Store', $store->id, $changed);
            \lib\Merchant::get($mid)->push(['event'=>'sync','data'=>$syncObject->makeSyncData($db)]);
            return ['success'=>true, 'message'=>'修改成功.', 'data'=>$store];
        }else{
            $row = [
                'mid'=>$mid,
                'name'=>$data['name'],
                'prop'=>(int)$data['prop'],
                'createtime'=>time(),
                'def'=>false,
            ];
            $db->startTrans();
            try{
                $row['sortid'] = (int)$db->table('tp_nb_goods_store')->where([['mid','=',$mid]])->field('COALESCE(MAX(sortid), 0) + 1 AS `sortid`')->find()['sortid'];
                if(null !== ($insid = $db->table('tp_nb_goods_store')->insert($row))){
                    $db->commit();
                    $row['id'] = $insid;
                    $store = static::createInstance($row, false);
                    $syncObject = new \lib\sync\Synchronize($mid);
                    $syncObject->put('Store', $store);
                    \lib\Merchant::get($mid)->push(['event'=>'sync', 'data'=>$syncObject->makeSyncData($db)]);
                    return ['success'=>true,'message'=>'添加成功', 'data'=>$store];
                }
                $db->rollback();
                return ['success'=>false,'message'=>'添加失败'];
            }catch(\PDOException $e){
                $db->rollback();
                return ['success'=>false,'message'=>'添加失败'];
            }
        }
    }

    public static function destroy(int $mid, int $id){
        $store = static::get($id);

        if(null === $store || $store->mid !== $mid){
            return ['success'=>false, 'message'=>'仓库不存在.'];
        }

        if(true === $store->def){
            return ['success'=>false, 'message'=>'默认仓库不能删除.'];
        }

        if($store->getNumGoods() > 0){
            return ['success'=>false,'message'=>'仓库中还有商品，不能删除'];
        }
        $store->remove();

        $syncObject = new \lib\sync\Synchronize($mid);
        $goods = \lib\data\StoreComponents::getByStoreId($store->id);
        foreach($goods as $g){
            $g->remove();
            $syncObject->rm('StoreComponents', $g->id);
        }
        $syncObject->rm('Store', $store->id);

        $db = new \sys\Db();
        $db->startTrans();
        try{
            $db->table('tp_nb_goods_store')->where([['id','=',$store->id]])->delete();
            $db->table('tp_nb_goods_store_map')->where([['store_id','=',$store->id]])->delete();
            $db->commit();
            \lib\Merchant::get($mid)->push(['event'=>'sync', 'data'=>$syncObject->makeSyncData($db)]);
            return ['success'=>true,'message'=>'删除成功'];
        }catch(\PDOException $e){
            $db->rollback();
            return ['success'=>false, 'message'=>'删除失败.'];
        }
    }

    public static function SaleGoods(
        int $mid, 
        int $uid, 
        int $scene, 
        int $settleMode, 
        ?int $payMode, 
        int $timeout, 
        ?string $authCode, 
        float $realPay, 
        array $goodsInfo, 
        string $desc,
        string $ipaddr, 
        \Closure $update,
        string $szIDNO = '',
        string $szName = '',
        string $szTel = '',
        string $szAddress = ''
        ) :array{
        $totalPrice = 0;
        $isNew = false;

        $db = new \sys\Db();
        $syncObject = new \lib\sync\Synchronize($mid, $db);
        $time = time();

        $objectCached = [];
        $tasks = [];
        $hasGift = false;

        $storeIds = array_unique(array_column($goodsInfo, 'store_id'));

        foreach($storeIds as $store_id){
            $store = Store::get($store_id);
            if(null === $store || $store->mid !== $mid){
                return ['success'=>false, 'message'=>'仓库不存在.'];
            }
            if($store->prop !== Store::IS_RACK){
                return ['success'=>false, 'message'=>"仓库\"{$store->name}\"的货物不能直接销售."];
            }
            $syncObject->put('Store', $store);
        }
        
        $stage = $scene == GoodsOrder::SCENE_BAR ? GoodsOrder::STAGE_FINISHED : GoodsOrder::STAGE_PAID;
        if($stage === GoodsOrder::STAGE_FINISHED){
            foreach($goodsInfo as $i=>$g){
                # 定位商品
                if(null === ($StoreComponents = StoreComponents::getGoodsFromStoreId($g['store_id'], $g['id'], $isNew, $db))){
                    return ['success'=>false, 'message'=>'商品不存在.'];
                }

                if($isNew){
                    $syncObject->put('StoreComponents', $StoreComponents);
                }

                $goods = Components::get($StoreComponents->goods_id);

                if(!$StoreComponents->on_sale){
                    return ['success'=>false, 'message'=>"商品{$goods->name}已下架."];
                }

                # 库存检查
                if($goods->prop == Components::PROP_COUNTON  && $StoreComponents->store_num < $g['num']){
                    return ['success'=>false, 'message'=>"商品{$goods->name}库存不足.", 'data'=>$StoreComponents];
                }
                
                if(false === $g['give']){
                    $goodsInfo[$i]['calc_price'] = $itemPrice = $g['num'] * $goods->sale_price;
                    $totalPrice += $itemPrice;
                } else {
                    $hasGift = true;
                    $goodsInfo[$i]['calc_price'] = 0;
                }

                $goodsInfo[$i]['goods']= $goods;
                $goodsInfo[$i]['StoreComponents']= $StoreComponents;
            }
        }else{
            foreach($goodsInfo as $i=>$g){
                $goods = Components::get($g['id']);
                if($goods->mid !== $mid){
                    return ['success'=>false, 'message'=>'商品不存在.'];
                }
                if(!$goods->on_sale){
                    return ['success'=>false, 'message'=>"商品{$goods->name}已下架."];
                }

                if($goods->num < $g['num']){
                    return ['success'=>false, 'message'=>"商品{$goods->name}库存不足."];
                }
                
                $goodsInfo[$i]['calc_price'] = $itemPrice = $g['num'] * $goods->sale_price;
                $totalPrice += $itemPrice;

                $goodsInfo[$i]['goods'] = $goods;
                $goodsInfo[$i]['give']  = false;
            }
        }

        # 核验价格.
        if($settleMode === GoodsOrder::SETTLE_MODE_CASH){
            if(abs($totalPrice - $realPay) > 0.01){
                return ['success'=>false, 'message'=>'支付金额不正确.', 'data'=>[$totalPrice , $realPay]];
            }
        }else{
            $realPay = $totalPrice;
        }

        $merchant = \lib\Merchant::get($mid);
        #TODO: 执行收款
        switch($settleMode){
        case GoodsOrder::SETTLE_MODE_CASH:      // 现金支付
            $flowSrc = GoodsOrder::PAY_SRC_CASH;
            $isrc = 0;
            break;
        case GoodsOrder::SETTLE_MODE_ONLINE:    // 在线支付
            $flowSrc = GoodsOrder::PAY_SRC_ONLINE;
            $isrc = $merchant->isrc;
            switch($payMode){
            case GoodsOrder::PAY_MODE_PAYCODE:   // 付款码支付
                try{
                    if(1 == $isrc){
                        $pay = \lib\pay\XinPay::micropay('S3095007', $realPay, $authCode, $ipaddr, $timeout, $mid, '商品销售','', $update);
                    }else{
                        return ['success'=>false, 'message'=>'尚未配置正确的支付渠道.'];
                    }
                }catch(PayException $e){
                    return ['success'=>false, 'message'=>$e->getMessage()];
                }
                break;
            case GoodsOrder::PAY_MODE_QRCODE:    // 用户扫码支付
                try{
                    if(1 == $isrc){
                        $pay = \lib\pay\XinPay::qrcodepay('S3095007', $realPay, $ipaddr, $timeout, $mid, '商品销售','吧台购买', $update);
                    }else{
                        return ['success'=>false, 'message'=>'尚未配置正确的支付渠道.'];
                    }
                }catch(PayException $e){
                    return ['success'=>false, 'message'=>$e->getMessage()];
                }
                break;
            default:
                return ['success'=>false, 'message'=>'支付方式不正确.'];
            }
            break;
        case GoodsOrder::SETTLE_MODE_MEMBER:     // 会员卡支付
            if(!$merchant->config->memberPay->on){
                return ['success'=>false, 'message'=>'商户未开通会员卡支付.'];
            }

            $flowSrc = GoodsOrder::PAY_SRC_MEMBER;
            $isrc = 0;
            if($payMode !== GoodsOrder::PAY_MODE_QRCODE){
                return ['success'=>false, 'message'=>'支付方式不正确.'];
            }
            try{
                $pay = \lib\pay\MemberPay::qrcodepay($merchant, $realPay, $ipaddr, $timeout, '商品销售','吧台购买', $update);
                $szIDNO = $pay->buyer_id;
                if($szName === ''){
                    $szName = $pay->buyer_name;
                }
            }catch(PayException $e){
                return ['success'=>false, 'message'=>$e->getMessage()];
            }
            break;
        default:
            return ['success'=>false, 'message'=>'不支持的付款途径.'];
        }

        $merchant->waitLock();
        $shift = $merchant->getCurrentShift();

        # 创建商品订单
        $order_no = \lib\Utils::genOrderNo('B');
        $tasks[] = $tp_nb_goods_order = $db->task('tp_nb_goods_order')->subInsert([
            'mid'           => $mid,
            'shift_id'      => $merchant->shift_id,
            'operator_uid'  => $uid,
            'settle_mode'   => $settleMode,
            'isrc'          => $isrc,
            'has_gift'      => $hasGift,
            'stage'         => $stage,
            'order_no'      => $order_no,
            'amount'        => $totalPrice,
            'realpay'       => $realPay,
            'refund'        => 0,
            'createtime'    => $time,
            'scene'         => $scene,
            'szName'        => $szName,
            'szIDNO'        => $szIDNO,
            'szTel'         => $szTel,
            'szAddress'     => $szAddress,
            'desc'          => $desc,
        ]);
        $order_id = new \sys\db\ExpValue($tp_nb_goods_order->pkVarName());

        # 创建商品订单明细
        if($stage == GoodsOrder::STAGE_FINISHED){
            $document_no = \lib\Utils::genOrderNo('S');
            $tasks [] = $tp_nb_goods_documents = $db->task('tp_nb_goods_documents')->subInsert([
                'mid'           => $mid,
                'document_no'   => $document_no,
                'document_type' => StoreComponents::DOC_TYPE_SALE_BAR,
                'shift_id'      => $merchant->shift_id,
                'operator_uid'  => $uid,
                'order_id'      => $order_id,
                'order_type'    => GoodsOrder::ORDER_TYPE,
                'settle_mode'   => $settleMode,
                'amount'        => $totalPrice,
                'realpay'       => 0,
                'desc'          => $desc,
                'createtime'    => $time,
            ]);
            $doc_id = new \sys\db\ExpValue($tp_nb_goods_documents->pkVarName());

            foreach($goodsInfo as $i=>$g){
                /** @var \lib\data\Components $goods */
                /** @var \lib\data\StoreComponents $StoreComponents */
                $goods = $g['goods'];
                $StoreComponents = $g['StoreComponents'];
                $num = $g['num'];
                $amount = $g['calc_price'];

                $goods->waitLock();
                $StoreComponents->waitLock();

                $newGoodsNum  = $goods->num - $num;
                $newStoreNum = $StoreComponents->store_num - $num;

                # 锁定商品库存后再次检查库存
                if($StoreComponents->store_num < $num){
                    if(isset($pay)) $pay->refund($db, $pay->amount, $ipaddr, '商品库存不足');
                    return ['success'=>false, 'message'=>"商品{$goods->name}库存不足."];
                }

                # 商品变化记录
                $tasks[] = $db->task('el_components_changes')->subInsert([
                    'mid'               => $mid,
                    'shift_id'          => $merchant->shift_id,
                    'document_id'       => $doc_id,
                    'document_type'     => StoreComponents::DOC_TYPE_SALE_BAR,
                    'operator_uid'      => $uid,
                    'goods_id'          => $goods->id,
                    'goods_name'        => $goods->name,
                    'settle_mode'       => $g['give']? GoodsOrder::SETTLE_MODE_GIFT : $settleMode,
                    'store_id'          => $store->id,
                    'store_name'        => $store->name,
                    'store_before'      => $StoreComponents->store_num,
                    'num'               => -$num,
                    'store_after'       => $newStoreNum,
                    'target_id'         => 0,
                    'target_name'       => '',
                    'target_before'     => 0,
                    'target_after'      => 0,
                    'calc_mode'         => StoreComponents::CALC_MODE_PIECE,
                    'pack_size'         => $goods->pack_size,               # 每件个数
                    'pack_num'          => 0,                               # 多少件
                    'amount'            => $amount,                         # 计算好的价格. 如果是赠送 该项为0
                    'createtime'        => $time,
                ]);

                if($goods->prop == Components::PROP_COUNTON){
                    # 生成库存更新任务
                    $oldValue = [];
                    if(null !==($changed = $StoreComponents->update(['store_num'=>$newStoreNum], false, $oldValue))){
                        $objectCached[] = [$StoreComponents, $changed, $oldValue];
                        $tasks[] = $db->table('tp_nb_goods_store_map')->where([['id','=',$StoreComponents->id]])->subUpdate($changed);
                    }

                    # 生成库存总表更新任务
                    $oldValue = [];
                    if(null !==($changed = $goods->update(['num'=>$newGoodsNum], false, $oldValue))){
                        $objectCached[] = [$goods, $changed, $oldValue];
                        $tasks[] = $db->table('tp_nb_goods_rack')->where([['id','=',$goods->id]])->subUpdate($changed);
                    }
                }
            }
        } else {
            # TODO 生成商品订单明细，用于配货.
            foreach($goodsInfo as $i=>$g) {
                /** @var \lib\data\Components $goods */
                $goods = $g['goods'];
                $num = $g['num'];
                $amount = $g['calc_price'];

                $goods->waitLock();
                $newGoodsNum = $goods->num;
                $tasks[] = $db->task('tp_nb_goods_order_distribution')->subInsert([
                    'mid'           => $mid,
                    'order_id'      => $order_id,
                    'order_type'    => GoodsOrder::ORDER_TYPE,
                    'goods_id'      => $goods->id,
                    'num'           => $num,
                    'done'          => 0,
                    'give'          => $g['give'],
                    'valid'         => true,
                    'sale_price'    => $goods->sale_price,
                    'createtime'    => $time,
                ]);

                if($goods->prop == Components::PROP_COUNTON){
                    # 生成库存总表更新任务
                    $oldValue = [];
                    if(null !==($changed = $goods->update(['num'=>$newGoodsNum], false, $oldValue))){
                        $objectCached[] = [$goods, $changed, $oldValue];
                        $tasks[] = $db->table('tp_nb_goods_rack')->where([['id','=',$goods->id]])->subUpdate($changed);
                    }
                }
            }
        }

        # 生成资金流水
        if($realPay > 0.00999) {
            $beforeAmount = $merchant->amount;
            $feeAmount = 0;

            # 计算手续费
            if($settleMode == GoodsOrder::SETTLE_MODE_ONLINE){
                $feeAmount = $realPay * 0.0038;
                $feeAmount = round($feeAmount, 4);
            }

            # 实际到账
            $finalAmount = $merchant->amount + $realPay - $feeAmount;

            # 更新商户账户余额
            if(null !== $field = GoodsOrder::$SETTLE_SRC[$flowSrc]){
                $desc = "商品销售:{$flowSrc}";
                $oldValue = [];
                if(null !== ($changed = $merchant->update([$field=>$finalAmount], false, $oldValue))){
                    $tasks[] = $db->table('tp_nb_merchant_goods')->where([['id','=',$merchant->id]])->subUpdate($changed);
                    $objectCached[] = [$merchant, $changed, $oldValue];
                }
            }

            # 更新当前班次汇总数据
            $oldValue = [];
            if(null !== ($changed = $shift->onSale($flowSrc, $realPay, $feeAmount, $oldValue))){
                $tasks[] = $db->task('tp_nb_goods_shift')->where($shift->id)->subUpdate($changed);
                $objectCached[] = [$shift, null, $oldValue];
            }

            $afterAmount = $beforeAmount + $realPay;
            $tasks[] = $db->task('tp_nb_goods_order_flow')->subInsert([
                'order_type'    => GoodsOrder::ORDER_TYPE,
                'order_id'      => $order_id,
                'is_fee'        => 0,
                'mid'           => $merchant->id,
                'shift_id'      => $merchant->shift_id,
                'src'           => $flowSrc,
                'amount'        => $realPay,
                'before'        => $beforeAmount,
                'after'         => $afterAmount,
                'createtime'    => $time,
            ]);
            

            # 生成服务费流水(3.8‰)
            if($feeAmount > 0.0000999){
                $beforeAmount   = $afterAmount;
                $afterAmount    = $beforeAmount - $feeAmount;
                $tasks[] = $db->task('tp_nb_goods_order_flow')->subInsert([
                    'order_type'    => GoodsOrder::ORDER_TYPE,              # 订单类型
                    'order_id'      => $order_id,                           # 订单号
                    'is_fee'        => 1,                                   # 服务费
                    'mid'           => $merchant->id,                       # 商户ID
                    'shift_id'      => $merchant->shift_id,                 # 班次ID
                    'src'           => $flowSrc,                            # 来源
                    'amount'        => -$feeAmount,                         # 金额
                    'before'        => $beforeAmount,                       # 之前
                    'after'         => $afterAmount,                        # 之后
                    'createtime'    => $time,                               # 创建时间
                ]);
            }
        }


        if(isset($pay)){
            $tasks[] = $pay->useSql($db, GoodsOrder::ORDER_TYPE, $order_id);
        }

        $db->startTrans();
        try{
            if($db->batch_execute([$tasks])){
                $db->commit();
                $tp_nb_goods_order_id = $tp_nb_goods_order->getId();
                if(isset($pay)){
                    $pay->use(GoodsOrder::ORDER_TYPE, $tp_nb_goods_order_id);
                }

                if($scene !== GoodsOrder::SCENE_BAR) {
                    # 远程购买。生成消息推送。
                    if($scene  == GoodsOrder::SCENE_APP ||$scene == GoodsOrder::SCENE_STA){
                        $descMsg = "来自{$szAddress}购买了商品, 请及时处理.";
                    }else{
                        $descMsg = "第三方平台购买了商品, 请及时处理.";
                    }
                    $tp_nb_goods_messages = [
                        'mid'       => $mid,
                        'title'     => '配货请求',
                        'desc'      => $descMsg,
                        'action'    => 'orderdetail',
                        'payload'   => [
                            'order_id'      => $tp_nb_goods_order_id,
                            'order_type'    => GoodsOrder::ORDER_TYPE,
                        ],
                        'once'          => false,
                        'show'          => false,
                        'createtime'    => $time,
                        'expire'        => $time + 86400, // 24小时
                    ];
                    $tp_nb_goods_messages['id'] = $db->table('tp_nb_goods_messages')->insert($tp_nb_goods_messages);
                    $tasks[] = \sys\db\SubTask::createInstance('tp_nb_goods_messages', $tp_nb_goods_messages, \sys\Db::SQL_INSERT);
                }
                # 发起同步
                $syncObject->makeSyncTask($tasks);

                # 发送同步
                foreach($objectCached as list($obj, $changed, $oldValue)){
                    if(null !== $changed){
                        $syncObject->up($obj->getSyncName(), $obj->id, $changed);
                    }
                }

                return ['success'=>true, 'message'=>'操作成功.'];
            }
            $db->rollback();
            if(isset($pay)){
                $pay->refund($db, $pay->amount, $ipaddr, '购买失败退款');
            }
        }catch(PDOException $e){
            Log::write(implode([$e->getMessage(),"\nFILE: ", $e->getFile(), "\nLINE: ", $e->getLine(),"\nTRACE: ", $e->getTraceAsString(),"\n"]),'ERROR');
            $db->rollback();
        }

        # 还原旧值
        foreach($objectCached as list($obj, $changed, $oldValue)){
            $obj->update($oldValue);
        }
        return ['success'=>false, 'message'=>'操作失败.'];
    }

    public static function StoreCheck(\lib\Merchant $merchant, int $uid, array $goodsInfo, string $desc){
        $tasks = [];
        $objectCached = [];

        $mid = $merchant->id;
        $isNew = false;
        $time = time();

        $sumAmount      = 0;
        $sumAmountIn    = 0;
        $sumAmountOut   = 0;
        
        $merchant->waitLock();
        $db = new \sys\Db();
        $syncObject = new \lib\sync\Synchronize($merchant->id, $db);

        foreach($goodsInfo as $i=>$g){
            # 定位商品
            $store_id = $g['store_id'];
            $store = Store::get($store_id);
            $stroeGoods = StoreComponents::waitGoodsFromStoreId($db, $store_id, $g['id'], $isNew);
            if(null === $stroeGoods || $stroeGoods->mid !== $mid){
                return ['success'=>false, 'message'=>'商品不存在.'];
            }

            if($isNew){
                $syncObject->put('StoreComponents', $stroeGoods);
            }

            # 获取库存变更值
            $goods = Components::get($stroeGoods->goods_id);

            if(!$goods->prop == Components::PROP_COUNTON){
                return ['success'=>false, 'message'=>"商品{$goods->name}不计库存."];
            }

            $goodsInfo[$i]['_StoreComponents']   = $stroeGoods;
            $goodsInfo[$i]['_goods']        = $goods;
            $goodsInfo[$i]['_store']        = $store;
            $goodsInfo[$i]['amount']        = $amount = $g['num'] * $goods->sale_price;
            $sumAmount += $amount;
            if($amount > 0){
                $sumAmountIn += $amount;
            }else{
                $sumAmountOut += abs($amount);
            }
        }

        $tasks[] = $tp_nb_goods_documents = $db->task('tp_nb_goods_documents')->subInsert([
            'mid'           => $mid,
            'document_no'   => \lib\Utils::genOrderNo('S'),
            'document_type' => StoreComponents::DOC_TYPE_CHECK,
            'shift_id'      => $merchant->shift_id,
            'operator_uid'  => $uid,
            'settle_mode'   => GoodsOrder::SETTLE_MODE_NOPAY,
            'amount'        => $sumAmount,
            'order_id'      => 0,
            'order_type'    => 256,
            'realpay'       => 0,
            'desc'          => $desc,
            'createtime'    => $time,
        ]);
        $doc_id = new \sys\db\ExpValue($tp_nb_goods_documents->pkVarName());

        foreach($goodsInfo as $i=>$g){
            $stroeGoods = $g['_StoreComponents'];
            $goods = $g['_goods'];
            $store = $g['_store'];
            $num = $g['num'];

            $newStoreNum = $stroeGoods->store_num + $num;
            $newGoodsNum = $goods->num + $num;
            $tasks[] = $db->task('el_components_changes')->subInsert([
                'mid'               => $mid,
                'shift_id'          => $merchant->shift_id,
                'document_id'       => $doc_id,
                'document_type'     => StoreComponents::DOC_TYPE_CHECK,
                'operator_uid'      => $uid,
                'goods_id'          => $goods->id,
                'goods_name'        => $goods->name,
                'settle_mode'       => GoodsOrder::SETTLE_MODE_NOPAY,
                'store_id'          => $stroeGoods->store_id,
                'store_name'        => $store->name,
                'store_before'      => $stroeGoods->store_num,
                'store_after'       => $newStoreNum,
                'num'               => $num,
                'target_id'         => 0,
                'target_name'       => '',
                'target_before'     => 0,
                'target_after'      => 0,
                'calc_mode'         => StoreComponents::CALC_MODE_PIECE,
                'pack_size'         => $goods->pack_size,                 # 每件个数
                'pack_num'          => 0,                               # 多少件
                'amount'            => $g['amount'],                    # 计算好的价格. 如果是赠送 该项为0
                'createtime'        => $time,
            ]);

            # 更新仓库库存
            $oldValue = [];
            if(null !== ($changed = $stroeGoods->update(['store_num'=>$newStoreNum], false, $oldValue))){
                $tasks[] = $db->table('tp_nb_goods_store_map')->where([['id','=',$stroeGoods->id]])->subUpdate($changed);
                $objectCached[] = [$stroeGoods, $changed, $oldValue];
            }

            # 更新总库存
            $oldValue = [];
            if(null !== ($changed = $goods->update(['num'=>$newGoodsNum], false, $oldValue))){
                $tasks[] = $db->table('tp_nb_goods_rack')->where([['id','=',$goods->id]])->subUpdate($changed);
                $objectCached[] = [$goods, $changed, $oldValue];
            }
        }

        $shift = $merchant->getCurrentShift();
        $oldValue = [];
        if(null !== ($changed = $shift->onStock($sumAmountIn, $sumAmountOut, $oldValue))){
            $tasks[] = $db->task('tp_nb_goods_shift')->where($shift->id)->subUpdate($changed);
            $objectCached[] = [$shift, null, $oldValue];
        }

        $db->startTrans();
        try{
            if($db->batch_execute([$tasks])){
                $db->commit();

                # 发起同步
                $syncObject->makeSyncTask($tasks);

                # 发送同步
                foreach($objectCached as list($obj, $changed, $oldValue)){
                    if(null !== $changed){
                        $syncObject->up($obj->getSyncName(), $obj->id, $changed);
                    }
                }

                return ['success'=>true, 'message'=>'操作成功.'];
            }
            $db->rollback();
        }catch(PDOException $e){
            Log::write(implode([$e->getMessage(),"\nFILE: ", $e->getFile(), "\nLINE: ", $e->getLine(),"\nTRACE: ", $e->getTraceAsString(),"\n"]),'ERROR');
            $db->rollback();
        }
        
        # 还原数据
        foreach($objectCached as list($obj, $changed, $oldValue)){
            $obj->update($oldValue);
        }
        return ['success'=>false, 'message'=>'操作失败.'];
    }

    # 退货
    public static function refund(int $mid, int $uid, int $order_id,array $goodsInfo, string $ipaddr) :array {
        $objectCached = [];
        $tasks = [];
        $refundSum = 0;
        $isNew = false;

        $db = new \sys\Db();
        $syncObject = new \lib\sync\Synchronize($mid, $db);
        $time = time();

        $order = $db->table('tp_nb_goods_order')->where([['id','=',$order_id]])->find();
        if(null == $order || $order['mid'] != $mid){
            return ['success'=>false, 'message'=>'订单不存在.'];
        }
        $goodsFlow = $db->table('el_components_changes')->where([[
            'document_id','IN',$db->table('tp_nb_goods_documents')->where([['order_id','=', $order_id],['order_type','=',GoodsOrder::ORDER_TYPE]])->field('id')->order('`id` ASC')->subSelect()
        ]])->select();

        $orderStage = $order['stage'];
        if($orderStage >= GoodsOrder::STAGE_FINISHED && count($goodsInfo) == 0){
            return ['success'=>false, 'message'=>'请选择要退货的商品.'];
        }

        $isrc = $order['isrc'];
        switch($settleMode = $order['settle_mode']){
        case GoodsOrder::SETTLE_MODE_CASH:      // 现金支付
            $flowSrc = GoodsOrder::PAY_SRC_CASH;
            break;
        case GoodsOrder::SETTLE_MODE_ONLINE:    // 在线支付
            $flowSrc = GoodsOrder::PAY_SRC_ONLINE;
            break;
        case GoodsOrder::SETTLE_MODE_MEMBER:     // 会员卡支付
            $flowSrc = GoodsOrder::PAY_SRC_MEMBER;
            break;
        }

        $merchant = \lib\Merchant::get($mid)->waitLock();

        if($orderStage >= GoodsOrder::STAGE_PAID && $orderStage < GoodsOrder::STAGE_FINISHED){
            # 未出货的订单, 全部退款.
            # 部分出货的订单, 未出货的商品直接退款
            $distri = $db->table('tp_nb_goods_order_distribution')->where([['order_id','=',$order_id],['order_type','=', 12], ['valid','=', 1]])->select();
            foreach($distri as $di){
                if($di['num'] - $di['done']  == 0){
                    continue;
                }
                if($di['valid'] && false == $di['give']){
                    $refundSum += ($di['num'] - $di['done']) * $di['sale_price'];
                    $tasks[] = $db->task('tp_nb_goods_order_distribution')->where($di['id'])->subUpdate(['valid'=>false]);
                }
            }
        }
        
        if(count($goodsInfo) > 0){
            # 计算得到可退款的数目
            $canRefundDict = [];
            foreach($goodsFlow as $flow){
                $store_id = $flow['store_id'];
                $goods_id = $flow['goods_id'];
                $document_type = $flow['document_type'];

                # 赠送的商品不可退款
                if($flow['settle_mode'] == GoodsOrder::SETTLE_MODE_GIFT) {
                    continue;
                }

                if(!isset($canRefundDict[$store_id][$goods_id])) {
                    $canRefundDict[$store_id][$goods_id] = [$document_type == StoreComponents::DOC_TYPE_RETURN ? - $flow['num'] :$flow['num'], $flow['amount'] / $flow['num']];
                } else {
                    if($document_type == StoreComponents::DOC_TYPE_RETURN){
                        $canRefundDict[$store_id][$goods_id][0] -= $flow['num'];
                    }else{
                        $canRefundDict[$store_id][$goods_id][0] += $flow['num'];
                    }
                }
            }
            
            # 检查商品生成退款商品流水.
            foreach($goodsInfo as $i=>$g){
                $store_id = $g['store_id'];
                $goods_id = $g['goods_id'];

                if(!isset($canRefundDict[$store_id]) && !isset($canRefundDict[$store_id][$goods_id])){
                    return ['success'=>false, 'message'=>'要退款的商品不存在.'];
                }
                if($canRefundDict[$store_id][$goods_id][0] < $g['num']){
                    return ['success'=>false, 'message'=>'要退款的商品数量不足.'];
                }

                $sumAmount = $g['num'] * $canRefundDict[$store_id][$goods_id][1];
                $refundSum += $sumAmount;

                $goodsInfo[$i]['_StoreComponents'] = $StoreComponents = StoreComponents::waitGoodsFromStoreId($db, $store_id, $goods_id, $isNew);
                $goodsInfo[$i]['_goods'] = $goods = Components::get($goods_id);
                $goodsInfo[$i]['_store'] = $store = Store::get($store_id);
                $goodsInfo[$i]['_sumAmount'] = $sumAmount;

                if($isNew) {
                    $syncObject->put('StoreComponents', $StoreComponents);
                }
            }

            $document_no = \lib\Utils::genOrderNo('S');
            $tasks[] = $tp_nb_goods_documents = $db->task('tp_nb_goods_documents')->subInsert([
                'mid'           => $mid,
                'document_no'   => $document_no,
                'document_type' => StoreComponents::DOC_TYPE_RETURN,
                'shift_id'      => $merchant->shift_id,
                'operator_uid'  => $uid,
                'order_id'      => $order_id,
                'order_type'    => GoodsOrder::ORDER_TYPE,
                'settle_mode'   => $settleMode,
                'amount'        => -$refundSum,
                'realpay'       => -$refundSum,
                'desc'          => '商品退货',
                'createtime'    => $time,
            ]);
            $doc_id = new \sys\db\ExpValue($tp_nb_goods_documents->pkVarName());

            # 生成商品退款流水
            foreach($goodsInfo as $i=>$g) {
                $StoreComponents = $g['_StoreComponents'];
                $goods = $g['_goods'];
                $store = $g['_store'];
                $sumAmount = $g['_sumAmount'];
                $num = $g['num'];

                $newStoreNum = $StoreComponents->store_num + $num;
                $newGoodsNum = $goods->num + $num;
                $tasks[] = $db->task('el_components_changes')->subInsert([
                    'mid'               => $mid,
                    'shift_id'          => $merchant->shift_id,
                    'document_id'       => $doc_id,
                    'document_type'     => StoreComponents::DOC_TYPE_RETURN,
                    'operator_uid'      => $uid,
                    'goods_id'          => $goods->id,
                    'goods_name'        => $goods->name,
                    'settle_mode'       => $order['settle_mode'],
                    'store_id'          => $store_id,
                    'store_name'        => $store->name,
                    'store_before'      => $StoreComponents->store_num,
                    'num'               => $num,
                    'store_after'       => $newStoreNum,
                    'target_id'         => 0,
                    'target_name'       => '',
                    'target_before'     => 0,
                    'target_after'      => 0,
                    'calc_mode'         => StoreComponents::CALC_MODE_PIECE,
                    'pack_size'         => $goods->pack_size,            # 每件个数
                    'pack_num'          => 0,                            # 多少件
                    'amount'            => $sumAmount,                    # 计算好的价格. 如果是赠送 该项为0
                    'createtime'        => $time,
                ]);

                if($goods->prop == Components::PROP_COUNTON){
                    # 更新货架库存
                    $oldValue = [];
                    if(null !== ($changed = $StoreComponents->update(['store_num'=>$newStoreNum], false, $oldValue))){
                        $tasks[] = $db->table('tp_nb_goods_store_map')->where([['id','=',$StoreComponents->id]])->subUpdate($changed);
                        $objectCached[] = [$StoreComponents, $changed, $oldValue];
                    }

                    if($StoreComponents->on_sale){
                        # 更新总库存
                        $oldValue = [];
                        if(null !== ($changed = $goods->update(['num'=>$newGoodsNum], false, $oldValue))){
                            $tasks[] = $db->table('tp_nb_goods_rack')->where([['id','=',$goods->id]])->subUpdate($changed);
                            $objectCached[] = [$goods, $changed, $oldValue];
                        }
                    }
                }
            }
        }

        # 存在退款, 则生成退款流水.
        if($refundSum > 0.0000999) {
            $tasks[] = $db->task('tp_nb_goods_order_flow')->subInsert([
                'order_type'    => GoodsOrder::ORDER_TYPE,
                'order_id'      => $order_id,
                'is_fee'        => 0,
                'mid'           => $merchant->id,
                'shift_id'      => $merchant->shift_id,
                'src'           => $flowSrc,
                'isrc'          => $isrc,
                'amount'        => -$refundSum,
                'before'        => $merchant->amount,
                'after'         => $merchant->amount - $refundSum,
                'createtime'    => $time,
            ]);

            $orderUpdate = ['refund'=>$order['refund'] + $refundSum];
            if(abs($order['refund'] + $refundSum) - $order['realpay'] < 0.0000999){
                $orderUpdate['stage'] = GoodsOrder::STAGE_CANCELLED;
            }

            if($order['operator_uid'] == 0){
                $orderUpdate['operator_uid'] = $uid;
            }

            $tasks[] = $db->task('tp_nb_goods_order')->where($order_id)->subUpdate($orderUpdate);
            
            # 更新班次汇总数据
            $shift = $merchant->getCurrentShift();
            $oldValue = [];
            if(null !== ($changed = $shift->onRefund($flowSrc, $refundSum, 0, $oldValue))){
                $tasks[] = $db->task('tp_nb_goods_shift')->where($shift->id)->subUpdate($changed);
                $objectCached[] = [$shift, null, $oldValue];
            }

            # 更新商户账户余额
            $oldValue = [];
            if(null !== $field = GoodsOrder::$SETTLE_SRC[$flowSrc]){
                $oldValue = [];
                if(null !== ($changed = $merchant->update([$field=>$merchant->$field - $refundSum], false, $oldValue))){
                    $tasks[] = $db->table('tp_nb_merchant_goods')->where([['id','=',$merchant->id]])->subUpdate($changed);
                    $objectCached[] = [$merchant, $changed, $oldValue];
                }
            }
        }

        if($settleMode == GoodsOrder::SETTLE_MODE_ONLINE){
            if(1 == $isrc){
                $tp_nb_pays_xin = $db->table('tp_nb_pays_xin')->where([['order_id','=',$order_id], ['order_type','=', GoodsOrder::ORDER_TYPE]])->find();
                $pay = new \lib\pay\XinPay($tp_nb_pays_xin);
            }else{
                return ['success'=>false, 'message'=>'无法退款, 该支付渠道已经下线.'];
            }
        }elseif($settleMode == GoodsOrder::SETTLE_MODE_MEMBER){
            $tp_nb_goods_pays_member = $db->table('tp_nb_goods_pays_member')->where([['order_id','=',$order_id], ['order_type','=', GoodsOrder::ORDER_TYPE]])->find();
            $pay = new \lib\pay\MemberPay($tp_nb_goods_pays_member);
        }


        $db->startTrans();
        try{
            if($db->batch_execute($tasks)){
                $db->commit();
                if(isset($pay)){
                    $pay->refund($db, $refundSum, $ipaddr, '商品退货');
                }

                # 发起同步
                $syncObject->makeSyncTask($tasks);

                # 发送同步
                foreach($objectCached as list($obj, $changed, $oldValue)){
                    if(null !== $changed){
                        $syncObject->up($obj->getSyncName(), $obj->id, $changed);
                    }
                }
                return ['success'=>true, 'message'=>'操作成功.'];
            }
        }catch(\Throwable $e){
            Log::write(implode([$e->getMessage(),"\nFILE: ", $e->getFile(), "\nLINE: ", $e->getLine(),"\nTRACE: ", $e->getTraceAsString(),"\n"]),'ERROR');
            $db->rollback();
        }

        # 还原数据
        foreach($objectCached as list($obj, $changed, $oldValue)){
            $obj->update($oldValue);
        }
        return ['success'=>false, 'message'=>'操作失败.'];
    }

    public static function barDistri(int $mid, int $uid, int $order_id, int $mode, array $distriInfo, string $ipaddr) : array {
        
        $refundSum = 0;
        $tasks = [];
        $objectCached = [];
        $totalPrice = 0;
        $isNew      = false;
        $db = new \sys\Db();
        $merchant = \lib\Merchant::get($mid)->waitLock();
        $time = time();
        $syncObject = new \lib\sync\Synchronize($mid, $db);

        $order = $db->table('tp_nb_goods_order')->where([['id','=',$order_id]])->find();
        
        if(null === $order || $order['mid'] !== $mid){
            return ['success'=>false, 'message'=>'订单不存在.'];
        }

        if($order['stage'] >= GoodsOrder::STAGE_FINISHED){
            return ['success'=>false, 'message'=>'订单已经完成, 无法配货.'];
        }

        switch($order['scene']){
        case GoodsOrder::SCENE_APP:
            $documentType = StoreComponents::DOC_TYPE_SALE_NET;
            break;
        case GoodsOrder::SCENE_STA:
            $documentType = StoreComponents::DOC_TYPE_SALE_STA;
            break;
        case GoodsOrder::SCENE_BAR:
            $documentType = StoreComponents::DOC_TYPE_SALE_BAR;
            break;
        }
        
        if($mode == 2){
            $isrc = $order['isrc'];
            switch($settleMode = $order['settle_mode']){
            case GoodsOrder::SETTLE_MODE_CASH:      // 现金支付
                $flowSrc = GoodsOrder::PAY_SRC_CASH;
                break;
            case GoodsOrder::SETTLE_MODE_ONLINE:    // 在线支付
                $flowSrc = GoodsOrder::PAY_SRC_ONLINE;
                break;
            case GoodsOrder::SETTLE_MODE_MEMBER:     // 会员卡支付
                $flowSrc = GoodsOrder::PAY_SRC_MEMBER;
                break;
            }
        }
        $distri = dict($db->table('tp_nb_goods_order_distribution')->where([['order_id','=', $order['id']], ['order_type','=', GoodsOrder::ORDER_TYPE]])->select(), 'id');

        foreach($distriInfo as $i=>$d){
            # 检查配货信息是否存在
            if(null == ($dis = &$distri[$d['id']] ?? null)){
                return ['success'=>false, 'message'=>'配货信息不存在.'];
            }

            # 安全检查
            if(($dis['num'] - $dis['done']) < $d['num']){
                return ['success'=>false, 'message'=>'配货数目超出购买数.'];
            }

            # 获取货架库存信息
            $stroeGoods = StoreComponents::waitGoodsFromStoreId($db, $d['store_id'], $dis['goods_id'], $isNew);
            if(null === $stroeGoods || $stroeGoods->mid !== $mid){
                return ['success'=>false, 'message'=>'商品不存在.'];
            }

            if($isNew){
                $syncObject->put('StoreComponents', $stroeGoods);
            }
            $goods = Components::get($stroeGoods->goods_id);

            if($goods->prop == Components::PROP_COUNTON && $stroeGoods->store_num < $d['num']) {
                return ['success'=>false, 'message'=>"商品\"{$goods->name}\"货架库存不足,无法配货."];
            }
            
            if(false == $dis['give']){
                $distriInfo[$i]['calc_price'] = $calcPrice = $dis['sale_price'] * $d['num'];
                $totalPrice += $calcPrice;
            }else{
                $distriInfo[$i]['calc_price'] = 0;
            }

            $dis['done'] += $d['num'];
            $distriInfo[$i]['_StoreComponents'] = $stroeGoods;
            $distriInfo[$i]['_goods'] = $goods;
            $distriInfo[$i]['_distri'] = $dis;
            $distriInfo[$i]['_store'] = Store::get($stroeGoods->store_id);
        }

        $document_no = \lib\Utils::genOrderNo('S');
        $tasks [] = $tp_nb_goods_documents = $db->task('tp_nb_goods_documents')->subInsert([
            'mid'           => $mid,
            'document_no'   => $document_no,
            'document_type' => $documentType,
            'shift_id'      => $merchant->shift_id,
            'operator_uid'  => $uid,
            'order_id'      => $order_id,
            'order_type'    => GoodsOrder::ORDER_TYPE,
            'settle_mode'   => $settleMode = $order['settle_mode'],
            'amount'        => $totalPrice,
            'realpay'       => 0,
            'desc'          => $order['desc'],
            'createtime'    => $time,
        ]);
        $doc_id = new \sys\db\ExpValue($tp_nb_goods_documents->pkVarName());
        foreach($distriInfo as $i=>$info){
            $StoreComponents = $info['_StoreComponents'];
            $goods = $info['_goods'];
            $store = $info['_store'];
            $dis = $info['_distri'];
            $num = $info['num'];

            $tasks[] = $db->task('tp_nb_goods_order_distribution')->where($info['_distri']['id'])->subUpdate(['done'=>$info['_distri']['done']]);

            # 商品变化记录
            $tasks[] = $db->task('el_components_changes')->subInsert([
                'mid'               => $mid,
                'shift_id'          => $merchant->shift_id,
                'document_id'       => $doc_id,
                'document_type'     => StoreComponents::DOC_TYPE_SALE_BAR,
                'operator_uid'      => $uid,
                'goods_id'          => $goods->id,
                'goods_name'        => $goods->name,
                'settle_mode'       => $dis['give']? GoodsOrder::SETTLE_MODE_GIFT : $settleMode,
                'store_id'          => $store->id,
                'store_name'        => $store->name,
                'store_before'      => $StoreComponents->store_num,
                'store_after'       => $newStoreNum = $StoreComponents->store_num - $num,
                'num'               => $num,
                'target_id'         => 0,
                'target_name'       => '',
                'target_before'     => 0,
                'target_after'      => 0,
                'calc_mode'         => StoreComponents::CALC_MODE_PIECE,
                'pack_size'         => $goods->pack_size,               # 每件个数
                'pack_num'          => 0,                               # 多少件
                'amount'            => $info['calc_price'],             # 计算好的价格. 如果是赠送 该项为0
                'createtime'        => $time,
            ]);

            if($goods->prop == Components::PROP_COUNTON){
                # 生成库存更新任务
                $oldValue = [];
                if(null !==($changed = $StoreComponents->update(['store_num'=>$newStoreNum], false, $oldValue))){
                    $objectCached[] = [$StoreComponents, $changed, $oldValue];
                    $tasks[] = $db->table('tp_nb_goods_store_map')->where([['id','=',$StoreComponents->id]])->subUpdate($changed);
                }
            }
            if($dis['num'] == $dis['done']) {
                $tasks[] = $db->task('tp_nb_goods_order_distribution')->where($dis['id'])->subUpdate(['valid'=>false]);
            }
        }
        
        $alldone = true;
        foreach($distri as $dis){
            if($dis['num'] > $dis['done']){
                $alldone = false;
                if($mode == 1){
                    break;
                }
                # 剩余部分退款.
                if(false == $dis['give']){
                    $refundSum = ($dis['num'] - $dis['done']) * $dis['sale_price'];
                }
            }
        }

        if($mode == 2 && $refundSum > 0.0000999) {
            $tasks[] = $db->task('tp_nb_goods_order_flow')->subInsert([
                'order_type'    => GoodsOrder::ORDER_TYPE,
                'order_id'      => $order_id,
                'is_fee'        => 0,
                'mid'           => $merchant->id,
                'shift_id'      => $merchant->shift_id,
                'src'           => $flowSrc,
                'isrc'          => $isrc,
                'amount'        => -$refundSum,
                'before'        => $merchant->amount,
                'after'         => $merchant->amount - $refundSum,
                'createtime'    => $time,
            ]);

            # 更新班次汇总数据
            $shift = $merchant->getCurrentShift();
            $oldValue = [];
            if(null !== ($changed = $shift->onRefund($flowSrc, $refundSum, 0, $oldValue))){
                $tasks[] = $db->task('tp_nb_goods_shift')->where($shift->id)->subUpdate($changed);
                $objectCached[] = [$shift, null, $oldValue];
            }

            # 更新商户账户余额
            $oldValue = [];
            if(null !== $field = GoodsOrder::$SETTLE_SRC[$flowSrc]){
                $oldValue = [];
                if(null !== ($changed = $merchant->update([$field=>$merchant->$field - $refundSum], false, $oldValue))){
                    $tasks[] = $db->table('tp_nb_merchant_goods')->where([['id','=',$merchant->id]])->subUpdate($changed);
                    $objectCached[] = [$merchant, $changed, $oldValue];
                }
            }
            $orderUpdate = ['refund'=>$order['refund'] + $refundSum];

            if($settleMode == GoodsOrder::SETTLE_MODE_ONLINE){
                if(1 == $isrc){
                    $tp_nb_pays_xin = $db->table('tp_nb_pays_xin')->where([['order_id','=',$order_id], ['order_type','=', GoodsOrder::ORDER_TYPE]])->find();
                    $pay = new \lib\pay\XinPay($tp_nb_pays_xin);
                }else{
                    return ['success'=>false, 'message'=>'无法退款, 该支付渠道已经下线.'];
                }
            }elseif($settleMode == GoodsOrder::SETTLE_MODE_MEMBER){
                $tp_nb_goods_pays_member = $db->table('tp_nb_goods_pays_member')->where([['order_id','=',$order_id], ['order_type','=', GoodsOrder::ORDER_TYPE]])->find();
                $pay = new \lib\pay\MemberPay($tp_nb_goods_pays_member);
            }
        }
            
        if($order['operator_uid'] == 0){
            $orderUpdate['operator_uid'] = $uid;
        }
        if($order['stage'] != ($newOrderStage = $alldone || ($mode == 2 || $refundSum > 0) ? GoodsOrder::STAGE_FINISHED : GoodsOrder::STAGE_WAIT_SHIP)){
            $orderUpdate['stage'] = $newOrderStage;
        }
        if(!empty($orderUpdate)){
            $tasks[] = $db->task('tp_nb_goods_order')->where($order_id)->subUpdate($orderUpdate);
        }

        $db->startTrans();
        try{
            if($db->batch_execute($tasks)){
                $db->commit();

                if($refundSum > 0.00009999 && isset($pay)){
                    $pay->refund($db, $refundSum, $ipaddr, '商品配货');
                }

                # 发起同步
                $syncObject->makeSyncTask($tasks);

                # 发送同步
                foreach($objectCached as list($obj, $changed, $oldValue)){
                    if(null !== $changed){
                        $syncObject->up($obj->getSyncName(), $obj->id, $changed);
                    }
                }
                return ['success'=>true, 'message'=>'操作成功.'];
            }
            $db->rollback();
        }catch(\Throwable $e){
            Log::write(implode([$e->getMessage(),"\nFILE: ", $e->getFile(), "\nLINE: ", $e->getLine(),"\nTRACE: ", $e->getTraceAsString(),"\n"]),'ERROR');
            $db->rollback();
        }

        # 还原数据
        foreach($objectCached as list($obj, $changed, $oldValue)){
            $obj->update($oldValue);
        }

        return ['success'=>false, 'message'=>'操作失败.'];
    }


}